Goodreads

library(tidyr)
library(dplyr)
library(ggplot2)
library(data.table)
library(lubridate)

Introduzione

Cos’è goodreads?

Goodreads è un social network dedicato ai libri creato nel dicembre 2006 dallo sviluppatore di software e imprenditore statunitense Otis Chandler.

Gli utenti, dopo la registrazione gratuita, possono aggiungere ai loro profili libri letti o da leggere, condividendo recensioni, commenti, votazioni, dati sull’acquisto e sulla lettura, suggerimenti con altri utenti, creare gruppi e partecipare a discussioni.

Il dataset

Il dataset preso in considerazione è tratto da kaggle.com` ed è composto da 20 colonne:

  • author_average_rating
  • author_gender
  • author_genres
  • author_id
  • author_name
  • author_page_url
  • author_rating_count
  • author_review_count
  • birthplace
  • book_average_rating
  • book_fullurl
  • book_id
  • book_title
  • genre1
  • genre2
  • num_ratings
  • num_reviews
  • pages
  • publish_date
  • score

Per sistemare il dataset:

  1. Separare i generi dell’autore nel genere prevalente e altri; se non ha altri generi allora inserire “unknown”
  2. Convertire le pagine da factor a int
  3. Cancellare eventuali caratteri speciali nel titolo dei libri,negli autori e paese di provenienza
  4. Cancellare gli spazi dalla colonna del paese di provenienza; se è vuoto allora “unknown”
  5. Della publish_date prelevare gli ultimi 4 caratteri, ovvero l’anno di pubblicazione. Si potrebbe pensare di convertire l’anno in data, ma non avendo mese e giorno completi di tutti i testi è preferibile tenere l’anno. Da notare però come molti libri non abbiano nemmeno l’anno di pubblicazione. In quel caso si inserisce “NA”. L’anno viene convertito in intero per l’ordinamento corretto.
# Leggere il csv
goodreads <- read.csv("good_reads_final.csv")
df <- goodreads

# Cambiare codifica del file
fwrite(df,"good.csv")
goodreads<- fread("good.csv",encoding="UTF-8")

# Modifiche elencate sopra
goodreads<-goodreads %>%
  mutate(author_genres = stringr::str_replace(author_genres,",","/")) %>%
  separate(author_genres,into=c("first_genre","other_genre"),sep="/") %>%
  mutate(other_genre = ifelse(is.na(other_genre) | other_genre=="", "unknown",other_genre)) %>%
  mutate(other_genre = stringr::str_replace_all(other_genre,",","")) %>%
  mutate(pages = as.integer(pages)) %>%
  mutate(author_name = stringr::str_replace_all(author_name, "\n","")) %>%
  mutate(birthplace = stringr::str_replace_all(birthplace, "\n",""))%>%
  mutate(book_title = stringr::str_replace_all(book_title, "\n","")) %>%
  mutate(first_genre = stringr::str_replace_all(first_genre,"-"," "),
         other_genre = stringr::str_replace_all(other_genre,"-"," ")) %>%
  mutate(birthplace = stringr::str_replace_all(birthplace, "  ","")) %>%
  mutate(birthplace = ifelse(birthplace == "","unknown",birthplace)) %>%
  separate(publish_date,into=c("other","year"),sep=-4) %>%
  select(-other, -author_page_url,-book_fullurl) %>%
  mutate(year = ifelse(year=="" | grepl("th",year),NA,year)) %>%
  mutate(year = as.integer(year))
## Warning: si è prodotto un NA per coercizione

## Warning: si è prodotto un NA per coercizione
head(goodreads,n=10)
##    author_average_rating author_gender        first_genre
## 1                   4.01        female historical fiction
## 2                   4.15          male literature fiction
## 3                   4.00        female            romance
## 4                   3.88          male            fiction
## 5                   4.10        female        young adult
## 6                   3.77          male             horror
## 7                   4.16        female            romance
## 8                   3.94        female         nonfiction
## 9                   3.78        female            fantasy
## 10                  4.08        female        young adult
##          other_genre author_id         author_name author_rating_count
## 1            unknown     74489   Victoria Thompson               74399
## 2  mystery thrillers    706255       Stieg Larsson             3726435
## 3            unknown   5618190 Mimi Jean Pamfiloff               76496
## 4             memoir     37871         José Donoso                5522
## 5            fantasy     36122   Patricia C. Wrede              291013
## 6            unknown     58947         Steve Niles               47938
## 7            unknown   4833990        Jillian Dodd              110522
## 8            unknown      7956          Mary Roach              321197
## 9            unknown    155651         Nancy Baker                1019
## 10           unknown    274533      Simone Elkeles              481114
##    author_review_count     birthplace book_average_rating  book_id
## 1                 6268  United States                4.02   686717
## 2               142704         Sweden                4.13  2429135
## 3                 7975  United States                3.99 27833684
## 4                  489          Chile                4.14   382975
## 5                13453  United States                4.01    64207
## 6                 3240  United States                3.80   831829
## 7                 9451        unknown                3.95 34804503
## 8                29747  United States                3.84  5981308
## 9                  104         Canada                3.77   266600
## 10               25166  United States                4.01   544424
##                                                 book_title        genre_1
## 1                               Murder on St. Mark's Place        Mystery
## 2                          The Girl with the Dragon Tattoo        Fiction
## 3                                     Tailored for Trouble        Romance
## 4                                The Obscene Bird of Night        Fiction
## 5        Sorcery & Cecelia: or The Enchanted Chocolate Pot        Fantasy
## 6                                 30 Days of Night, Vol. 1 Sequential Art
## 7                                                 Stalk Me    Young Adult
## 8            Bonk: The Curious Coupling of Science and Sex     Nonfiction
## 9                                      Kiss of the Vampire     Paranormal
## 10                                        Leaving Paradise    Young Adult
##            genre_2 num_ratings num_reviews pages year score
## 1       Historical        5260         375   277 2000  3230
## 2          Mystery     2229163       65227   465 2005  3062
## 3     Contemporary        2151         391   354 2016  4585
## 4  Magical Realism        1844         173   438 1970  1533
## 5      Young Adult       17051        1890   326 1988  2105
## 6   Sequential Art       17122         561   104 2004  4372
## 7          Romance       11684        1107   327 2012  2396
## 8          Science       45963        4268   319 2008  2054
## 9           Horror         594          42   278 1993  1311
## 10         Romance       40093        2375   303 2007  1994

Quesiti

Quali sono gli autori più famosi?

authors<-goodreads %>%
  select(author_average_rating:birthplace, book_id) %>%
  select(author_id, author_name,everything()) %>%
  group_by(author_name, author_gender) %>%
  summarise(ratings=sum(author_rating_count), reviews = sum(author_review_count),n_books=n()) %>%
  arrange(-ratings, -reviews)

authors
## # A tibble: 12,156 x 5
## # Groups:   author_name [12,156]
##    author_name         author_gender   ratings reviews n_books
##    <chr>               <chr>             <int>   <int>   <int>
##  1 J.K. Rowling        female        126640336 3098497       6
##  2 Stephen King        male           72795154 2655847       6
##  3 Suzanne Collins     female         61288176 2223663       6
##  4 Stephenie Meyer     male           57284674 1835702       6
##  5 J.R.R. Tolkien      male           43074264  655372       6
##  6 Rick Riordan        male           42373362 1701792       6
##  7 Dan Brown           male           36774808 1009774       6
##  8 Cassandra Clare     female         35621747 1988989       6
##  9 John Green          male           34033693 1938303       6
## 10 William Shakespeare male           33669143  576948       6
## # ... with 12,146 more rows

Top 10 degli scrittori con valutazioni e recensioni a confronto

most_rated_authors <- authors %>%
  arrange(-ratings) %>%
  head(n=10) %>%
  gather("ratings","reviews", key="type",value="number")

ggplot(most_rated_authors, aes(x=reorder(author_name,-number)))+
  geom_bar(stat="identity", position="dodge",aes(y=number/1000000,fill=type)) +
  labs(x = "Nome dell'autore", y="Numero di rating", title="Numero di valutazioni e recensioni (in milioni)")+
  geom_text(aes(label=author_name), stat="count", size=3,angle=90, vjust=-0.5, hjust=-0.2)+
  theme(axis.text.x=element_blank())

Gli autori con più ratings sono anche i più recensiti?

library(modelr)
rew_rat<- lm(reviews~ratings,authors)

authors %>% add_predictions(rew_rat) %>%
  ggplot(aes(ratings))+
    geom_jitter(aes(y=reviews), alpha = 0.5)+
    geom_line(aes(y=pred), color="red3", size=1.6)

Dall’analisi risultano esserci più recensioni laddove le valutazioni sono in numero minore. Vi sono delle eccezioni, che probabilmente sono quelle precedentemente analizzate. Provando ad applicare una scala logaritmica ai dati, si nota che il modello si adatta meglio rispetto alla relazione lineare precedentemente evidenziata.

authors %>% add_predictions(rew_rat) %>%
  ggplot(aes(ratings))+
    geom_jitter(aes(y=reviews), alpha=0.5)+
    geom_line(aes(y=pred), color="red3", size=1.6)+
    scale_y_log10(limits=c(1,NA))

Alcune recensioni vanno in negativo perché molti libri hanno meno di 10 recensioni. Proviamo a indagare sulla distribuzione dei dati delle reviews:

summary(authors$reviews)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##       0     303    1291   17644    6427 3098497

I dati sono concentrati tra 303 e 17644, mentre il massimo supera i 3 milioni. Ci sono però anche libri che non hanno recensioni.

Proviamo a rappresentare anche i residui:

authors %>% add_predictions(rew_rat) %>%
  add_residuals(rew_rat) %>%
  ggplot(aes(ratings))+
    geom_ref_line(h=0)+
    geom_point(aes(y=resid))

Dal grafico dei residui emerge che i dati sono concentrati laddove i ratings e le recensioni sono minoritarie. Inoltre un punto in particolare si discosta dagli altri.

summary(rew_rat)
## 
## Call:
## lm(formula = reviews ~ ratings, data = authors)
## 
## Residuals:
##      Min       1Q   Median       3Q      Max 
## -1287503    -6308    -5782    -3239   986714 
## 
## Coefficients:
##              Estimate Std. Error t value Pr(>|t|)    
## (Intercept) 6.441e+03  3.275e+02   19.67   <2e-16 ***
## ratings     3.458e-02  1.476e-04  234.36   <2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 35720 on 12154 degrees of freedom
## Multiple R-squared:  0.8188, Adjusted R-squared:  0.8188 
## F-statistic: 5.493e+04 on 1 and 12154 DF,  p-value: < 2.2e-16

Secondo il modello, la bontà del modello si aggira attorno all’82%.

In che modo il numero di valutazioni influisce sulla valutazione media?

rating<- authors %>%
  select(author_name, reviews) %>%
  left_join(select(goodreads, author_name, author_average_rating)) %>%
  unique()
## Joining, by = "author_name"
rating
## # A tibble: 12,277 x 3
## # Groups:   author_name [12,156]
##    author_name         reviews author_average_rating
##    <chr>                 <int>                 <dbl>
##  1 J.K. Rowling        3098497                  4.45
##  2 Stephen King        2655847                  4.03
##  3 Suzanne Collins     2223663                  4.26
##  4 Stephenie Meyer     1835702                  3.64
##  5 J.R.R. Tolkien       655372                  4.32
##  6 Rick Riordan        1701792                  4.32
##  7 Dan Brown           1009774                  3.81
##  8 Cassandra Clare     1988989                  4.26
##  9 John Green          1938303                  4.07
## 10 William Shakespeare  576948                  3.86
## # ... with 12,267 more rows
ggplot(rating, aes(y=reviews,x=author_average_rating ))+
  geom_jitter(aes(color=reviews))

summary(rating$author_average_rating)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##   1.820   3.790   3.950   3.948   4.110   5.000

Come si nota dal grafico, le valutazioni sono elevate, infatti la media è su 4. C’è da notare però che nonostante le valutazioni siano alte, il numero di review è molto basso, quindi chi tende a valutare il libro generalmente non lo recensisce.

C’è una relazione tra il numero di libri scritti e il successo dell’autore?

authors %>%
  arrange(-reviews) %>%
  ggplot(aes(x = n_books, y=reviews))+
    geom_bar(stat="identity", aes(fill=reviews))+
    scale_x_discrete(breaks = c(seq(1,6)))+
    labs(x="Numero di libri scritti", y="Valutazioni")

Sembra che all’aumentare dei libri scritti aumentino anche le valutazioni. Probabilmente perchè gli scrittori sono incentivati a scrivere se ricevono feedback positivi dei precedenti capitoli. Questo ovviamente solo nel caso in cui siano famosi. Come emerge dal grafico ci sono comunque molti autori che pur avendo scritto molti libri non hanno eccellenti valutazioni rispetto ad altri.

Da notare però che se gli autori che scrivono molti libri sono tra i più recensiti, sono anche una cerchia ristretta, perché la maggioranza degli scrittori si limita a scrivere 1 o 2 libri (media e mediana).

summary(as.integer(authors$n_books))
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##   1.000   1.000   1.000   1.883   2.000   6.000
authors$n_books = as.factor(authors$n_books)
authors %>%
  arrange(-reviews) %>%
  ggplot(aes(x = n_books))+
    geom_bar(aes(fill = reviews), stat="count", fill="blue3")+
    scale_x_discrete(breaks = c(seq(1,6)))+
    labs(x="Numero di libri scritti", y="Scrittori")+
    geom_text(aes(label=..count..),stat="count",vjust=-0.2)

ggplot(authors, aes(n_books,reviews))+
  geom_boxplot(outlier.color = "blue", outlier.shape = NA)+
  coord_flip()+
  scale_y_discrete(breaks=seq(1,10,by=2))

Dal boxplot emerge comunque che la mediana aumenta a ogni libro in più scritto. In particolar modo la distribuzione dei dati con 6 libri è più variabile e contiene svariati outlier.

Analisi sui generi

library(gridExtra)

par(mfrow = c(2,1))

genres1 <- goodreads %>%
  count(genre_1) %>%
  rename(genre = genre_1)

genres2 <- goodreads %>%
  count(genre_2) %>%
  rename(genre = genre_2)

genres <- full_join(genres1,genres2, by="genre") %>%
  mutate(n.x = ifelse(is.na(n.x),0,n.x),
         n.y = ifelse(is.na(n.y),0,n.y))%>%
  group_by(genre) %>%
  summarise(n = n.x+n.y)

loved_genres<- genres %>%
  arrange(-n) %>%
  head(10)

hollow_genres <- genres %>%
  arrange(n) %>%
  head(10)

loved_genres2<- loved_genres %>%
  ggplot(aes(x=reorder(genre,n),y=n,fill=reorder(genre,n)))+
    geom_bar(stat="identity", show.legend = TRUE)+
    coord_polar(theta="y")+
    theme_void()+
    labs(title="Generi più famosi",
         fill="Genere")

hollow_genres2 <- hollow_genres %>%
   ggplot(aes(x=reorder(genre,n),y=n,fill=reorder(genre,n)))+
    geom_bar(stat="identity", show.legend = TRUE)+
    coord_polar(theta="y")+
    theme_void()+
    labs(title="Generi di nicchia",
         fill="Genere")

grid.arrange(loved_genres2,hollow_genres2,ncol=2)

Dai due grafici a ciambella emergono i generi più letti e quelli più di nicchia, ovvero i cui testi sono rari. Per un’analisi più approfondita: Shiny App

Gender gap?

Analizziamo ora il sesso degli scrittori, per analizzare se vi sono più scrittrici o scrittori e se c’è una relazione tra genere e successo.

authors %>%
  group_by(author_gender) %>%
  summarise(n=n())
## # A tibble: 2 x 2
##   author_gender     n
##   <chr>         <int>
## 1 female         5439
## 2 male           6717

Da una prima analisi emerge che vi sono più scrittori maschi che femmine. Analizziamo ora la relazione tra numero di libri scritti, reviews e genere dello scrittore.

authors %>%
  ggplot(aes(x = n_books))+
    geom_bar(stat="count",position="dodge", aes(fill=author_gender))+
    labs(x="Numero di libri scritti", y="Scrittori")+
    geom_text(aes(label=..count..), stat="count",
              position=position_dodge(width=1))

Non sembra esserci una differenza sostanziale, ma proviamo a esplorare la relazione tra numero di reviews e sesso.

ggplot(authors, aes(x=author_gender, y=reviews, color=author_gender))+
  geom_boxplot(outlier.size=2)+
  scale_y_log10()

summary(authors$reviews)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##       0     303    1291   17644    6427 3098497

La distribuzione dei dati delle scrittrici sembra essere leggermente superiore rispetto a quella maschile, anche se la mediana sembra coincidere. Gli outlier superiori, quindi gli scrittori di successo, non sembrano differire di numero, mentre quelli inferiori sembrano essere il doppio rispetto agli scrittori maschi.

#Sopra il quantile 75%
authors[authors$reviews > quantile(authors$reviews,0.75) | authors$reviews < quantile(authors$reviews,0.25) ,] %>%
  transform(quantile = ifelse(reviews > quantile(authors$reviews,0.75),0.75,0.25)) %>%
  group_by(author_gender, quantile) %>%
  summarise(n=n())
## # A tibble: 4 x 3
## # Groups:   author_gender [2]
##   author_gender quantile     n
##   <chr>            <dbl> <int>
## 1 female            0.25  1222
## 2 female            0.75  1447
## 3 male              0.25  1814
## 4 male              0.75  1592

La maggioranza dei valori al di fuori del box per i maschi si trova al di sotto del quantile 0.25, mentre per le femmine si trova sopra il quantile 0.75.

Proviamo ad analizzare le frequenze relative:

n_writers <- nrow(authors)

authors %>%
  ggplot(aes(x = n_books))+
    geom_bar(position="dodge", aes(y=..count..,fill=author_gender))+
    labs(x="Numero di libri scritti", y="Scrittori")

In termini di frequenze relative si può concludere che il numero di valutazioni.

Nazionalità degli scrittori

goodreads %>%
  select(birthplace) %>%
  count(birthplace) %>%
  arrange(-n) %>%
  head(30)
## # A tibble: 30 x 2
##    birthplace            n
##    <chr>             <int>
##  1 " United States"  11471
##  2 unknown            4414
##  3 " United Kingdom"  2164
##  4 Canada              646
##  5 Japan               428
##  6 Australia           420
##  7 Germany             263
##  8 Egypt               250
##  9 India               230
## 10 Ireland             207
## # ... with 20 more rows

Di molti scrittori non è nota la nazionalità, ma la maggioranza proviene dagli stati uniti. Sembra comunque prevalere la lingua inglese sulle altre, probabilmente perchè l’applicazione è ideata da uno statunitense e pensata per essere monolingua. Tralasciando le nazionalità sconosciute:

top30countries <- goodreads %>%
  select(birthplace, author_gender) %>%
  count(birthplace, author_gender) %>%
  filter(birthplace !="unknown") %>%
  arrange(-n) %>%
  head(30)


ggplot(top30countries,aes(birthplace,n))+
  geom_bar(stat="identity", position="dodge", aes(fill=author_gender))+
  geom_segment(aes(x=birthplace, 
                   xend=birthplace, 
                   y=min(n), 
                   yend=max(n)), 
               linetype="dashed", 
               size=0.05,
               color="grey")+
  coord_flip()+
  theme_classic()

Quali sono i libri valutati meglio?

library(DT)

book_list <- goodreads %>%
  select(book_title,author_name, book_average_rating,genre_1,genre_2, pages, num_ratings) %>%
  rename(rating = book_average_rating) %>%
  arrange(-rating) %>%
  unique()
DT::datatable(book_list)

Analizziamo però la relazione tra i rating e il numero di recensioni: se un libro ha poche recensioni, ma positive, il suo rating rimane alto, pur essendo letto da pochi. Se un libro ha molte recensioni, tutte variabili, nonostante abbia successo potrebbe avere dei ratings bassi.

ggplot(goodreads,aes(book_average_rating))+
  geom_histogram(fill="lightblue")+
  geom_freqpoly(color="blue")

Dal grafico emerge un’asimmetria negativa, che tende a sopravvalutare il libro, svalutando il ratings degli altri. Sembra esserci però una sorta di simmetria attorno al 4.

Correlazione tra rating dell’autore e del singolo libro

rating_cor<- goodreads %>%
  select(author_average_rating,book_average_rating) %>%
  mutate(author_average_rating = round(author_average_rating,0),
         book_average_rating = round(book_average_rating,0)) %>%
  rename(autore = author_average_rating,libro = book_average_rating)

library(corrplot)
corrplot(cor(rating_cor),method="color")

Sembra esserci una correlazione tra la valutazione degli scrittori e dei libri, una relazione crescente. Quindi al crescere del rating del libro, aumenta il rating del suo autore.

Evoluzione del numero di libri nel tempo

goodreads %>%
  group_by(year) %>%
  summarise(n_books = n()) %>%
  ggplot(aes(year,n_books))+
    geom_line()+
    scale_x_continuous()

summary(goodreads$year)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max.    NA's 
##    -720    2000    2011    1990    2013    2019     744

Verso gli anni 2000 c’è stata una crescita esponenziale del numero di libri pubblicati.

Quali sono i libri più letti?

top_books<-goodreads %>%
  select(book_id,book_title,num_ratings) %>%
  group_by(book_title, book_id) %>%
  summarise(ratings = sum(num_ratings))%>%
  arrange(-ratings) %>%
  left_join(select(goodreads,author_name,book_title)) %>%
  unique() %>%
  head(n=20)
## Joining, by = "book_title"
DT::datatable(top_books)
library(plotly)

top_books_plot<-top_books %>% 
  left_join(select(authors,author_name,author_gender), by="author_name") %>%
  ggplot(aes(x=book_title, y=ratings, text=author_name, color=author_gender)) + 
    geom_point(size=3) + 
    geom_segment(aes(x=book_title, 
                     xend=book_title, 
                     y=0, 
                     yend=ratings)) + 
    labs(title="Top books")+
    theme()+
    coord_flip()

ggplotly(top_books_plot, tooltip = "text")